package infoserver;

import java.io.*;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.swing.JOptionPane;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * Dit is de klasse waar de settings tab in de GUI wordt voltooid. 
 * @author Roman Wiedijk
 * @since 07-05-2012
 */

public class Settings {
    //Algemene variabelen aanmaken.
    private String total = "";
    
    //Variabelen voor encryptie / decryptie.
    private SecretKey key;
    private Cipher cipher;
    
    private Infoserver is;
    
    
    /**
    * Constructor die de basis van de encryptie creëert. 
    * @author Roman Wiedijk
    * @since 07-05-2012
    */
    public Settings(Infoserver is){
        this.is = is;
        try {
            //Sleutel maken, die omzetten naar bytes in UTF-8 formaat.
            byte[] keyBytes = "PenguinsKomenJeHalen153987bluuurgh".getBytes("UTF-8");
            //Hier wordt een statische DESede key van gemaakt.
            DESedeKeySpec keySpec = new DESedeKeySpec(keyBytes);
            SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
            key = factory.generateSecret(keySpec);
            //Cipher initializeren.
            cipher = Cipher.getInstance("DESede");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    /**
    * Haalt de huidige locatie op van de applicatie om hier de locatie van de log bestanden van te maken 
    * @author Erik Uitenbroek
    * @since 07-05-2012
    */  
    
    public String getPath(){
        String path = System.getProperty("user.dir");
        String pathFinal = path + "\\log";
        return pathFinal;
    }
    
    /**
    * Schrijft settings uit de GUI velden weg in een .cfg bestand. 
    * @author Roman Wiedijk
    * @since 07-05-2012
    */
    
    public void writeSettings(GUI g, boolean firstRun){
        BufferedWriter bw;
        try {
            //BufferedWriter openen, false zorgt ervoor dat deze over de oude gegevens heen gaan.
            bw = new BufferedWriter(new FileWriter("Settings.cfg", false));
            //Velden aflezen en deze wegschrijven.
            bw.write(
                    "IPAdres: " + g.getFieldIp()
                    + "\r\n"
                    + "Port: " + g.getFieldPort()
                    + "\r\n"
                    + "Database: " + g.getFieldDbname()
                    + "\r\n"
                    + "Username: " + g.getFieldUser()
                    + "\r\n"
                    + "Password: " + encrypt(g.getFieldPass())
                    + "\r\n"
                    + "Log: " + getPath()
                    );
            //Sluiten.
            bw.close();
            //Als hier geen errors zijn, is het succesvol.
            if(!firstRun){
                JOptionPane.showMessageDialog( null, "Save succesfull.");
                is.getGui().writeLog("Settings opgeslagen");
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } 
    }
    
    /**
    * Neemt .cfg bestand op in de Settings velden. 
    * @author Roman Wiedijk
    * @since 07-05-2012
    */
    public void readSettings(GUI g){
        BufferedReader br;
        String regel;
        try {
            //BufferedReader aanmaken.
            br = new BufferedReader(new FileReader("Settings.cfg"));
            //Alles inlezen.
            while((regel = br.readLine()) != null){
                total += regel + '\n';
            }
            //Sluiten
            br.close();
        } catch (FileNotFoundException ex) {
            //Als geen bestand is gevonden (eerste run) bestand aanmaken.
            writeSettings(g, true);
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            //Variabelen voor substring declareren.
            boolean startDetected = false;
            int lineNumber = 0;
            int firstLetter = 0;
            int lastLetter = 0;
            
            //Als sprake is van een puntkomma, dan komt twee letters daarna het in te lezen woord.
            for(int i = 0; i<total.length(); i++){  
                if(total.substring(i, i+1).equals(":")){
                    //De eerste ":" wordt onthouden als startpunt.
                    if(startDetected == false){
                        firstLetter = i+2;
                        startDetected = true;
                    }
                }
                //Indien het teken voor een nieuwe regel plaatsvind, dan is de letter voor die nieuwe regel het einde van een woord.
                if(total.substring(i, i+1).equals("\n")){
                    lastLetter = i;
                    //Het regelnummer word opgeteld, zodat onderscheid tussen variabelen plaatsvind.
                    lineNumber++;
                    //Het startpunt wordt voor de volgende regel gereset.
                    startDetected = false;
                    //Variabelen in tekstvak plaatsen.
                    switch (lineNumber){
                        case 1: String ipAdress = total.substring(firstLetter, lastLetter);
                                g.setFieldIp(ipAdress);
                                break;
                        case 2: String portNumber = total.substring(firstLetter, lastLetter);   
                                g.setFieldPort(portNumber);
                                break;
                        case 3: String databaseName = total.substring(firstLetter, lastLetter);
                                g.setFieldDbname(databaseName);
                                break;
                        case 4: String userName = total.substring(firstLetter, lastLetter);
                                g.setFieldUser(userName);
                                break;
                        case 5: String passWord = total.substring(firstLetter, lastLetter);
                                g.setFieldPass(decrypt(passWord));
                                break;
                   }
                }
            }
        }
    }
    
    /**
    * Neemt wachtwoord op en beveiligd deze. Returned het resultaat. 
    * @author Roman Wiedijk
    * @since 07-05-2012
    */
    
    public String encrypt(String unencryptedText){
        String outputText = "";
        try{
            //Zet Cipher op Encrypt modus.
            cipher.init(Cipher.ENCRYPT_MODE, key);
            //Zet de ingevoerde tekst om naar UTF-8 formaat bytes.
            byte[] defaultText = unencryptedText.getBytes("UTF-8");
            //Encrypt het wachtwoord.
            byte[] encryptedText = cipher.doFinal(defaultText);
            //Zet het encrypted wachtwoord om naar BASE64 String formaat, om de bytes te behouden.
            //Als dit niet gebeurt, kan het wachtwoord niet op deze manier ge-decrypt worden (padding).
            BASE64Encoder encoder = new BASE64Encoder();
            outputText = encoder.encode(encryptedText);
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
        //Geeft de encrypted tekst terug om weggeschreven te worden.
        return outputText;
    }
    
    /**
    * Decrypt de tekst uit de encrypt functie. 
    * @author Roman Wiedijk
    * @since 07-05-2012
    */
    
    public String decrypt(String encryptedText){
       String decryptedMessage = "";
        try{
            //Zet Cipher op Decrypt modus.
            cipher.init(Cipher.DECRYPT_MODE, key);
            //Zet de BASE64 String weer terug naar bytes die een multiple van 8 zijn, zodat geen padding errors voorkomen.
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] encrypted = decoder.decodeBuffer(encryptedText);
            //Decrypt bytes met Cipher.
            byte[] decrypted = cipher.doFinal(encrypted);
            //Zet de bytes om naar een normale String.
            decryptedMessage = new String(decrypted);
        } catch (Exception ex){
            ex.printStackTrace();
        }
        //Geeft de waarde terug om in het tekstvak te stoppen.
        return decryptedMessage;
    }
}